#include "../main.h"

#define CLASS HttpServer


//static JSBool	construct	(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
static void		finalize	(JSContext *cx, JSObject *obj);


static JSClass __class = {
    "HttpServer",
    JSCLASS_HAS_PRIVATE,// | JSCLASS_CONSTRUCT_PROTOTYPE,
    JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, finalize,
    JSCLASS_NO_OPTIONAL_MEMBERS
};


struct T {
	int port;
	pthread_t tid;
};



static void finalize(JSContext *cx, JSObject *obj){
	trace1("finalize");
	jsval rval;
//	Close(cx, obj, 0, NULL, &rval);
}



//////////////////////////////////////////////////////////////////////////////
//
static JSBool construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
	struct T *x = calloc(sizeof(struct T), 1);
	JS_SetPrivate(cx, obj, x);
	return JS_TRUE;
}


static void *talker(void *param){
	int fd = (int)param;	
	trace1("talker");
	
	JSContext *cx = get_context();
	
	jsval argv[1], rval;
	argv[0]=INT_TO_JSVAL(fd);
	JSBool ok = JS_CallFunctionName(cx, global, "onrequest", 1, argv, &rval);	
	
	if (JS_TRUE==JS_IsExceptionPending(cx)) {
		if(JS_TRUE==JS_GetPendingException(cx, &rval)) {
			trace("Uncaught exception: %s\n", str(rval));
			close(fd);
			fd=0;
		}
		JS_ClearPendingException(cx);
	}

	if(fd) {
		trace("FD %i: closing", fd);	
		close(fd);
		fd=0;
	}
	usleep(100000);

	trace("leaving thread");
	pthread_exit(0);
	return 0;
}


static void *thread_Start(void *param){
	struct T *x = (struct T *)param;
	trace1(__FILE__"::thread_Start");

	int s, c, n;
	struct sockaddr_in sa;
	struct sockaddr_in cli;
	fd_set sockfds;
	struct timeval tv;
	char buf1[256], buf2[256];
	struct sockaddr_in my_addr;
	struct sockaddr_in socks[50];
	pthread_t thread[50];
	int i=1;
	pthread_attr_t pattr;
		
	trace2("port %i", x->port);
	s=socket(PF_INET, SOCK_STREAM, 0);
	bzero(&sa, sizeof sa);
	sa.sin_family = AF_INET;
	sa.sin_port   = htons(x->port);
	sa.sin_addr.s_addr = htonl(INADDR_ANY);

	i=1;	
	setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int));
	
	bind(s, (struct sockaddr *)&sa, sizeof sa);
	
//	pthread_setconcurrency(100);
	trace2("listening");
	listen(s, 100);

	i=0;

	/* set thread create attributes */
	pthread_attr_init(&pattr);
	pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED);
         
	while(1){
		n=sizeof(cli);
		if((c=accept(s,(struct sockaddr *)&cli, &n)) < 0)
			fatal("accept()");
//		talker((void*)c);
		
		trace("FD %i: accept with new thread", c);
		if(pthread_create(&(thread[i++]), &pattr, talker, (void *)c ) != 0)
			fatal("pthread_create()");
		if(i>40)i=0;

	}
	trace("exiting start_httpserver()");
	return NULL;
}




//////////////////////////////////////////////////////////////////////////////
//
static JSBool Start(JSContext *cx, uintN argc, jsval *vp) {
	struct T *x = JS_GetPrivate(cx, JS_THIS_OBJECT(cx, vp));
	trace1("HttpServer::Start");

	uint32 port;
	JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "u", &port);

	pthread_attr_t pattr;
	pthread_attr_init(&pattr);
	pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED);
	pthread_create(&x->tid, &pattr, thread_Start, x);
	return JS_TRUE;
}



//////////////////////////////////////////////////////////////////////////////
//
static JSBool Stop(JSContext *cx, uintN argc, jsval *vp) {
	trace1("close");
	struct T *x = JS_GetPrivate(cx, JS_THIS_OBJECT(cx, vp));


	bzero(x, sizeof(struct T));
	return JS_TRUE;
}


static JSFunctionSpec methods[] = {
	JS_FN("start",	Start,	1,0,0),
	JS_FN("stop", 	Stop, 	0,0,0),	
	JS_FS_END
};


//////////////////////////////////////////////////////////////////////////////
//
int httpserver_init(JSContext *cx, JSObject *global){
	JSObject *proto = JS_InitClass(cx, global, NULL, 
		&__class, construct, 0,
		props, NULL, NULL, NULL
	);
	return 0;
}



